/**
 * 
 */
package com.googlecode.eventio.internal;

import java.util.concurrent.Callable;
import java.util.concurrent.Executor;

import com.googlecode.eventio.CallableReaction;
import com.googlecode.eventio.EventDispatcher;
import com.googlecode.eventio.EventExecutor;
import com.googlecode.eventio.Loadable;
import com.googlecode.eventio.LoadableReaction;
import com.googlecode.eventio.Reaction;
import com.googlecode.eventio.Resource;
import com.googlecode.eventio.Result;

/**
 * @author steph
 * 
 */
public class EventExecutorImpl implements EventExecutor {

	private final EventDispatcher dispatcher;
	private final Executor executor;

	public EventExecutorImpl(EventDispatcher dispatcher, Executor executor) {
		this.dispatcher = dispatcher;
		this.executor = executor;
	}

	/**
	 * 
	 * @see java.util.concurrent.Executor#execute(java.lang.Runnable)
	 */
	@Override
	public void execute(Runnable command) {
		executor.execute(command);
	}

	/**
	 * 
	 * @see com.googlecode.eventio.EventExecutor#execute(java.util.concurrent.Callable
	 *      , com.googlecode.eventio.Reaction)
	 */
	@Override
	public <V> void execute(final Callable<V> c, final Reaction<Result<V>> react) {
		execute(new CallableReaction<V>() {

			@Override
			public V call() throws Exception {
				return c.call();
			}

			@Override
			public void react(Result<V> input) {
				react.react(input);
			}
		});
	}

	/**
	 * 
	 * @see com.googlecode.eventio.EventExecutor#execute(com.googlecode.eventio.
	 *      CallableReaction)
	 */
	@Override
	public <V> void execute(final CallableReaction<V> callReac) {

		final Object event = new Object();
		dispatcher.once(event, callReac);

		executor.execute(new Runnable() {

			@Override
			public void run() {
				Result<V> result;

				try {
					result = new Result<V>(callReac.call());
				} catch (Exception e) {
					result = new Result<V>(e);
				}

				dispatcher.emit(event, result);
			}
		});

	}

	public <IN, OUT> void execute(final LoadableReaction<IN, OUT> callReac) {

		final Object event = new Object();
		dispatcher.once(event, callReac);

		executor.execute(new Runnable() {

			@Override
			public void run() {
				Result<OUT> result;

				try {
					result = new Result<OUT>(callReac.load(null));
				} catch (Exception e) {
					result = new Result<OUT>(e);
				}

				dispatcher.emit(event, result);
			}
		});
	}

	@Override
	public <P, V> void execute(final P param, final Loadable<P, V> c,
			final Reaction<Result<V>> react) {
		execute(param, new LoadableReaction<P, V>() {

			@Override
			public V load(P param) throws Exception {
				return c.load(param);
			}

			@Override
			public void react(Result<V> input) {
				react.react(input);
			}
		});
	}

	@Override
	public <P, R> void execute(final P param,
			final LoadableReaction<P, R> callReac) {

		final Object event = new Object();
		dispatcher.once(event, callReac);

		executor.execute(new Runnable() {

			@Override
			public void run() {
				Result<R> result;

				try {
					result = new Result<R>(callReac.load(param));
				} catch (Exception e) {
					result = new Result<R>(e);
				}
				dispatcher.emit(event, result);
			}
		});
	}

	@Override
	public <P, V> Resource<P, V> newResource(final Loadable<P, V> loadable) {
		return new Resource<P, V>() {

			@Override
			public void get(final P input, Reaction<Result<V>> reaction) {
				final Object event = new Object();
				dispatcher.once(event, reaction);

				executor.execute(new Runnable() {

					@Override
					public void run() {
						try {
							V value = loadable.load(input);
							dispatcher.emit(event, new Result<V>(value));
						} catch (Exception e) {
							dispatcher.emit(event, new Result<V>(e));
						}
					}
				});
			}
		};
	}

}
